#include "zmqLogic.h"
#include "zmq.h"

/**
 * 默认的日志输出函数
 * 将日志内容写入标准输出流(stdout)
 * @param data 要输出的日志数据
 * @param len 日志数据的长度
 * @returns [void] 无返回值
 * @example
 * Write-Log "Hello" 5
 * @author Your Name
 * @version 1.0.0
 */

/**
 * 默认构造函数
 */
zmqLogic::zmqLogic() {}

/**
 * 默认析构函数
 * 等待接收线程处理完毕就释放资源
 */
zmqLogic::~zmqLogic()
{
    StopWork(); // 双重保险，析构是再尝试一次

    // 资源释放
    if (m_socket)
        zmq_close(m_socket);
    if (m_context)
        zmq_ctx_destroy(m_context);
}

/**
 * zmq对象初始化
 * @param ipcPath 套接字文件，如net.ipc
 * @param zmqType 本zmq队列的类型，如PUB、SUB、REQ
 * @param isServer 标记是否是服务端
 */
void zmqLogic::Initialize(const std::string &ipcPath, int zmqType, bool isServer)
{
    m_ipcPath = ipcPath;
    m_zmqType = zmqType;
    m_isServer = isServer;
}

/**
 * 绑定函数指针(收发都需要)
 * @param sendCallback 发送RPC消息函数指针
 * @param receCallback 接收RPC消息函数指针
 * @return 返回是否绑定成功
 */
bool zmqLogic::RegisterRecvCallback(ReceCallback receCallback)
{
    if (!receCallback)
    {
        Log_Error(0, "zmq的RegisterRecvCallback函数绑定失败");
        return false;
    }
    m_receCallback = receCallback;
    return true;
}

/**
 * 进行绑定监听函数（仅服务端调用）
 * @return 返回是否绑定监听成功
 */
bool zmqLogic::StartBind()
{
    if (!m_isServer)
        return false; // 是服务方才可以开启监听

    // 1.建立上下文
    m_context = zmq_ctx_new();
    m_socket = zmq_socket(m_context, m_zmqType);
    if (!m_socket)
    {
        Log_Error(0, "zmq的RegisterRecvCallback函数创建上下文失败");
        return false;
    }

    // 2.绑定套接字，组合成ipc://log.ipc
    std::string address = "ipc://" + m_ipcPath;
    if (zmq_bind(m_socket, address.c_str()) != 0)
    {
        zmq_close(m_socket);
        zmq_ctx_destroy(m_context);
        Log_Error(0, "zmq的RegisterRecvCallback函数绑定套接字失败");
        return false;
    }

    // 3.设置发送缓冲区高水位标记（HWM）和发送超时
    int sndhwm = 10000;  // 增大缓冲区
    int sndtimeo = 1000; // 1秒超时
    zmq_setsockopt(m_socket, ZMQ_SNDTIMEO, &sndtimeo, sizeof(sndtimeo));
    zmq_setsockopt(m_socket, ZMQ_SNDHWM, &sndhwm, sizeof(sndhwm));

    m_connected.store(true); // 绑定成功即标记为已连接
    return true;
}

/**
 * 进行连接函数（仅客户端调用）
 * @return 返回是否连接成功
 */
bool zmqLogic::StartConnect()
{
    if (m_isServer)
        return false; // 客户方才可以连接

    // 1.建立上下文
    m_context = zmq_ctx_new();
    m_socket = zmq_socket(m_context, m_zmqType);
    if (!m_socket)
    {
        Log_Error(0, "zmq的StartConnect函数创建上下文失败");
        return false;
    }

    // 2.链接套接字
    std::string address = "ipc://" + m_ipcPath;
    if (zmq_connect(m_socket, address.c_str()) != 0)
    {
        zmq_close(m_socket);
        zmq_ctx_destroy(m_context);
        Log_Error(0, "zmq的StartConnect函数绑定套接字失败");
        return false;
    }

    // 3.设置发送缓冲区高水位标记（HWM）和发送超时
    int sndhwm = 10000;  // 增大缓冲区
    int sndtimeo = 1000; // 1秒超时
    zmq_setsockopt(m_socket, ZMQ_SNDTIMEO, &sndtimeo, sizeof(sndtimeo));
    zmq_setsockopt(m_socket, ZMQ_SNDHWM, &sndhwm, sizeof(sndhwm));

    m_connected.store(true); // 连接成功即标记为已连接
    return true;
}

/**
 * 接收线程的处理循环入口
 * @return 返回是否开启成功
 */
bool zmqLogic::StartWorkCycle()
{
    // 如果没有绑定接收处理函数或者线程终止就不能开启
    if (!m_receCallback)
        return false;

    // 设置接收为非阻塞模式（很重要）
    int timeOut = 1000; // 超时时间是1秒
    if (zmq_setsockopt(m_socket, ZMQ_RCVTIMEO, &timeOut, sizeof(timeOut)) != 0)
    {
        Log_Error(0, "zmq的StartWorkCycle函数设置非阻塞模式失败");
        return false; // 设置非阻塞失败就直接退出
    }

    // 设置线程开始执行循环接收函数
    m_workThread = std::thread(&zmqLogic::DealFunc, this);
    return true;
}

/**
 * 子线程执行的循环函数
 */
void zmqLogic::DealFunc()
{
    zmq_msg_t msg; // 在外层定义接收对象，避免重复定义
    while (!m_stopFlag.load())
    {
        // 1.初始化（重置）接收对象
        zmq_msg_init(&msg);
        // 2.开始阻塞接收
        if (zmq_msg_recv(&msg, m_socket, 0) == -1)
        {
            // 如果是下面两种错误就不管，继续循环
            if (errno == ETERM || errno == EAGAIN)
            {
                zmq_msg_close(&msg);
                continue;
            }
            break; // 其他错误退出循环
        }

        // 3.走到这说明成功得到了消息
        size_t size = zmq_msg_size(&msg);
        const char *data = static_cast<const char *>(zmq_msg_data(&msg));
        char *buffer = new char[size]; // 动态分配内存
        memcpy(buffer, data, size);    // 复制数据
        m_receCallback(buffer, size);  // 交由处理函数处理
    }
    zmq_msg_close(&msg);
}

/**
 * 进程间发送消息函数
 * @param buffer 数据包
 * @param size 数据包大小
 * @return 返回成功发送的字节数或错误代码
 */
int zmqLogic::SendMsg(const char *buffer, size_t size)
{
    // 先判断连接是否建立了
    if (!m_connected.load())
    {
        int waitCount = 0;
        while (!m_connected.load() && waitCount++ < 500)
        { // 5秒超时
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        if (!m_connected.load())
        {
            Log_Error(0, "发送失败: ZMQ连接未建立");
            return -1; // 超时了还没建立连接就报错吧
        }
    }

    // 发送最多重试3次
    for (int i = 0; i < 3; i++)
    {
        // 先判断是否有其他线程在发送
        int ret = -1;
        {
            std::lock_guard<std::mutex> lock(m_sendMutex);
            ret = zmq_send(m_socket, buffer, size, ZMQ_DONTWAIT);
        }
        if (ret != -1)
            return ret; // 发送成功
        int zmq_err = zmq_errno();
        Log_Error(0, "DEBUG: ZMQ发送尝试%d失败: %s", i + 1, zmq_strerror(zmq_err));
        if (errno != EAGAIN)
            break;                                                  // 非缓冲区满错误，直接退出
        std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 短暂等待
    }
    return -1; // 重试失败
}

/**
 * 终止接收线程的处理
 */
void zmqLogic::StopWork()
{
    m_stopFlag.store(true); // 开启终止标志
    // 等待线程处理完本次任务
    if (m_workThread.joinable())
    {
        m_workThread.join();
        m_workThread = std::thread(); // 将线程对象置为不可连接状态
    }
}

/**
 * 原始发送函数
 */
void zmqLogic::Send(const std::string &str, int flag)
{
    zmq_send(m_socket, str.c_str(), str.size(), flag);
}
